/*
 * Copyright (c) 2011 Imre Fazekas.
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  Neither the name of the Brillien nor the names of its
 *  terms and concepts may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.streamline.json;

import com.vii.streamline.services.error.StreamLineException;
import com.vii.streamline.services.json.JsonServices;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.svenson.JSONParseException;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import static com.vii.streamline.json.JsonRpcServices.*;


public class JsonRpcProcessor {

    protected boolean                           ssoRequired;
    protected Logger                            logger;
    protected JsonRpcActor                      actor;
    
    {
        logger = LoggerFactory.getLogger( this.getClass() );
    }

    /**
     * Tells whether the SSO validation is required.
     */
    protected boolean isSsoRequired() {
        return ssoRequired;
    }

    public void setSsoRequired(boolean ssoRequired) {
        this.ssoRequired = ssoRequired;
    }

    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    public JsonRpcActor getActor() {
        return actor;
    }

    public void setActor(JsonRpcActor actor) {
        this.actor = actor;
    }

    protected boolean isBatchedJsonRpc( Object request ) throws StreamLineException {
        return request != null && request instanceof List;
    }

    protected boolean isJsonRpcRequest( HashMap<String, Object> request ) throws StreamLineException {
        return request != null && request.containsKey("method");
    }
    protected boolean isJsonRpcResponse( HashMap<String, Object> request ) throws StreamLineException {
        return request != null && (request.containsKey("result") || request.containsKey("error"));
    }

    /**
     * Processes a simple JSON-RPC request message.
     */
    protected JsonRpcResponse processSimpleRequest( JsonRpcRequest request ) {
        try{
            if (!request.isNotification()) {
                return actor.processQuery( request );
            }
        }catch (StreamLineException se){
            se.printStackTrace();
            return JsonRpcResponse.createResponse( request, ERROR_INTERNAL_ERROR.getCode(), ERROR_INTERNAL_ERROR.getMessage(), se.getMessage() );
        }
        return null;
    }

    /**
     * Processes a batch JSON-RPC message.
     */
    protected List<JsonRpcResponse> processBatchRequest( List<JsonRpcRequest> requests ) throws StreamLineException {
        LinkedList<JsonRpcResponse> responses = new LinkedList<JsonRpcResponse>();

        for( JsonRpcRequest request : requests ){
            JsonRpcResponse response = processSimpleRequest( request );
            if( response == null )
                response = JsonRpcResponse.createResponse( request, ERROR_INVALID_REQUEST.getCode(), ERROR_INVALID_REQUEST.getMessage(), request );
            responses.add( response );
        }

        return responses;
    }


    /**
     * Processes a JSON-RPC message returning the JsonRpcResponse object if exists.
     */
    public Object process(String requestString) {
        try {
            Object requestObj = JsonServices.parseJSON(requestString);
            if( isBatchedJsonRpc( requestObj ) ){
                List<JsonRpcRequest> requests = JsonRpcServices.getBatchJsonRpcRequests( requestString );
                logger.debug("Received batched JSON-RPC requests:" + requests );
                List<JsonRpcResponse> responses = processBatchRequest( requests );
                return responses;
            }
            else if( isJsonRpcRequest((HashMap<String, Object>) requestObj) ){
                JsonRpcRequest request = JsonRpcServices.getSimpleJsonRpcRequest( requestString );
                logger.debug("Received simple JSON-RPC request: " + request);
                JsonRpcResponse response = processSimpleRequest( request );
                return response;
            } else if( isJsonRpcResponse((HashMap<String, Object>) requestObj) ){
                JsonRpcResponse response = JsonRpcServices.getSimpleJsonRpcResponse( requestString );
                actor.processResponse( response );
            }
        } catch (JSONParseException e) {
            return JsonRpcResponse.createResponse( null, ERROR_PARSE.getCode(), ERROR_PARSE.getMessage(), requestString );
        } catch (StreamLineException e) {
            e.printStackTrace();
            return JsonRpcResponse.createResponse( null, e.getErrorCode(), e.getMessage(), e.getPayLoad() );
        }
        return null;
    }

}
